package com.uxsino.commons.qr;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Map;

import javax.imageio.ImageIO;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.io.Files;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.Writer;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.uxsino.commons.utils.Images;

/**
 *
 * @author <a mailto="royrxc@gmail.com">Roy</a>
 * @since 2016年10月1日
 */
public class ZxQr {
	private static final Logger LOG = LoggerFactory.getLogger(ZxQr.class);
	
	private String _encoding = "GB2312";

	private BarcodeFormat _qr_format = BarcodeFormat.QR_CODE;

	private Map<EncodeHintType, Object> _hints = Maps.newHashMap();

	private int _bar_color =  MatrixToImageConfig.BLACK;

	private int _blank_color =  MatrixToImageConfig.WHITE;

	private int _w = 200,_h=200;

	private int _margin = 1;
	
    private String _qr_content;
	
	private BufferedImage _qr_img;//原始大小，不包含边框
	
	private String imgExtension = "PNG";
	
	private ZxQr(){
	}
	
	public static ZxQr of(){
		return new ZxQr();
	}
	
	public static ZxQr of(String content){
		return of().content(content);
	}
	
	public ZxQr hint(EncodeHintType type, Object obj){
		this._hints.put(type, obj);
		return this;
	}

	public ZxQr hint(Map<EncodeHintType, Object> hints){
		this._hints.putAll(hints);
		return this;
	}

	public ZxQr imgExtension(String extension){
	    this.imgExtension = extension;
	    return this;
	}
	
	public ZxQr margin(int margin){
	    this._margin = margin;
	    return this;
	}
	
	public ZxQr qrImg(BufferedImage _qr_img){
		this._qr_img = _qr_img;
		return this;
	}
	
	public ZxQr qrImg(String qrImgAbsolutePath){
		try {
			this._qr_img = ImageIO.read(new File(qrImgAbsolutePath));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return this;
	}
	
	public ZxQr format(BarcodeFormat format){
		this._qr_format = format;
		return this;
	}
	
	public ZxQr size(int w, int h){
		this._w = w;
		this._h = h;
		return this;
	}
	
	public ZxQr color(int bar_color, int blank_color){
		this._bar_color = bar_color;
		this._blank_color = blank_color;
		return this;
	}
	
	public ZxQr content(String qrCnt){
		this._qr_content = qrCnt;
		return this;
	}
	
	public ZxQr charset(String charset){
	    this._encoding = charset;
	    return this;
	}
	
	/**
	 * 生成二维码
	 */
	public ZxQr encode(){
		this._qr_content = Strings.nullToEmpty(this._qr_content);
		this._qr_format = this._qr_format == null? BarcodeFormat.QR_CODE:this._qr_format;
		this._bar_color = this._bar_color==-1? MatrixToImageConfig.BLACK : this._bar_color;
		this._blank_color = this._blank_color==-1? MatrixToImageConfig.WHITE : this._blank_color;
		
		if(this._hints == null || this._hints.size() ==0 ){
			this._hints = Maps.newHashMap();
		}
		//设置编码格式
		if(this._hints.get(EncodeHintType.CHARACTER_SET) == null){
			this.hint(EncodeHintType.CHARACTER_SET, this._encoding);
		}
		//边框间距
		if(this._hints.get(EncodeHintType.MARGIN) == null){
			this.hint(EncodeHintType.MARGIN, this._margin);
		}
		
		Writer writer = /*BarcodeFormat.QR_CODE.equals(_qr_format) ? new QrCodeGenWrapper():*/new MultiFormatWriter();
		try {
			BitMatrix martrix = writer.encode(this._qr_content, this._qr_format, this._w, this._h, this._hints);
			martrix = deleteWhite(martrix);
			try {
				MatrixToImageConfig config = new MatrixToImageConfig(this._bar_color, this._blank_color);
				BufferedImage img = MatrixToImageWriter.toBufferedImage(martrix, config);

				this._qr_img = img;
				this.revise();//处理白边，重新绘制二维码大小
			} catch (Exception e) {
				LOG.error("编码二维码失败：",e);
			}
		} catch (WriterException e) {
		    LOG.error("编码二维码失败：",e);
		}
		return this;
	}
	
	public BufferedImage getQrImg(){
		if(this._qr_img == null){
			this.encode();
		}
		return this._qr_img;
	}
	
	public void saveQrImg(String destQrImgFileAbsolutePath){
		BufferedImage bufImg = getQrImg();
		if(bufImg != null){
			try {
                ImageIO.write(bufImg, Files.getFileExtension(destQrImgFileAbsolutePath),
                    new File(destQrImgFileAbsolutePath));
			} catch (IOException e) {
				LOG.debug("保存二维码失败：", e);
			}
		}
	}
	
	/**
	 * 将二维码覆盖到
	 * @param backGroundImg
	 * @param xstart
	 * @param ystart
	 * @param drawBackGround
	 * @return
	 */
	public BufferedImage coverToImg(BufferedImage backGroundImg, int xstart, int ystart, boolean drawBackGround){
		try {
			if(backGroundImg == null){
				LOG.warn("无背景图片");
				return null;	
			}
			if(this._qr_content == null && this._qr_img == null){
				LOG.warn("覆盖的二维码内容为空");
				return backGroundImg;
			}
			if(this._qr_img == null){
				this.getQrImg();
			}
			if(this._qr_img == null){
				LOG.warn("生成二维码失败！");
				return backGroundImg;
			}
			
			int dh = this._qr_img.getHeight();
			int dw = this._qr_img.getWidth();
			
			int bh = backGroundImg.getHeight();
			int bw = backGroundImg.getWidth();
			
			for (int h = 0; h < dh; h++) {
				for (int w = 0; w < dw; w++) {
                    if ((w + xstart < bw && h + ystart < bh)
                            && (drawBackGround || (!drawBackGround && this._qr_img.getRGB(w, h) != -1))) {
						backGroundImg.setRGB(w+xstart, h+ystart, this._qr_img.getRGB(w, h));
					}
				}
			}
		} catch (Exception e) {
			LOG.error("二维码转换成图片失败：", e);
		}
		return backGroundImg;
	}

	/**
	 * 删除白边
	 * @param matrix
	 * @return
	 */
	private static BitMatrix deleteWhite(BitMatrix matrix) {
		int[] rec = matrix.getEnclosingRectangle();
		int resWidth = rec[2];
		int resHeight = rec[3];

		BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
		resMatrix.clear();
		for (int i = 0; i < resWidth; i++) {
			for (int j = 0; j < resHeight; j++) {
				if (matrix.get(i + rec[0], j + rec[1]))
					resMatrix.set(i, j);
			}
		}
		return resMatrix;
	}
	
	public String decode(){
		if(this._qr_img == null){
			LOG.warn("未设置解码图片");
			return null;
		}
		
		LuminanceSource source = new BufferedImageLuminanceSource(this._qr_img);
		BinaryBitmap bitMap = new BinaryBitmap(new HybridBinarizer(source));
		
		MultiFormatReader reader = new MultiFormatReader();
		try {
			Result res = reader.decodeWithState(bitMap);
			String text = res.getText();
			this._qr_content = text;
			return text;
		} catch (NotFoundException e) {
			LOG.error("二维码解码错误：", e);
		}
		return null;
	}
	
	public byte[] toBytes(){
        BufferedImage img = this.getQrImg();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(img, this.imgExtension, out);
            return out.toByteArray();
        } catch (IOException e) {
            LOG.error("转换成bayte数组失败：", e);
        }
        return null;
    }

	public String toBase64ImageData(){
        byte[] bytes = toBytes();
        StringBuffer buffer = new StringBuffer();
        buffer.append("data:image/");
        buffer.append(this.imgExtension);
        buffer.append(";base64,");
        buffer.append(new Base64().encodeToString(bytes));
        return buffer.toString();
    }

	/**
	 * 修正尺寸和颜色
	 * @return
	 */
	public ZxQr revise(){
		BufferedImage img = this.getQrImg();

		if(this._w == img.getWidth() && this._h == img.getHeight()){
			return this;
		}

        img = Images.of(img, this.imgExtension).stretchTo(this._w - this._margin * 2, this._h - this._margin * 2)
            .getImage();

		int dh = img.getHeight();
		int dw = img.getWidth();
		BufferedImage res = new BufferedImage(this._w, this._h, img.getType());

		for (int h = 0; h < dh; h++) {
			for (int w = 0; w < dw; w++) {
				if(w < dw && h < dh){
					/*int color = img.getRGB(w, h);
					if(color == MatrixToImageConfig.WHITE || color == 0){
						color = this._blank_color;
					}else{
						color = this._bar_color;
					}
					res.setRGB(w+this._margin, h+this._margin, color);*/
					res.setRGB(w+this._margin, h+this._margin, img.getRGB(w, h));
				}
			}
		}
		this._qr_img = res;
		return this;
	}

	/*public static void main(String[] args) {
        String content = "[2018-11-22 11:00:00]-警告 : 翻开本书，你一定会被十大文豪鲜为人知的另一面惊得目瞪口呆：众人敬仰的陀思妥耶夫斯基嗜赌成性，喜欢预支稿费却不按时交稿；巴尔扎克虚荣浮华，经常欠钱不还；间·奥斯汀穿着土气，品位毛姆对巨匠们近乎不留情面的、一阵见血的评述并非有意哗众取宠，而是要刨去身份和作品，之留下真实的去躯干，将他们最为真实的、人性化的一面呈现在读者面前。用毛姆的话说，知道作者是个什么样的人，...(部分内容已省略)";
		String img = ZxQr.of(content.toString()).size(180, 180).color(0xFFFFFFFF, 0x00000000).margin(4).toBase64ImageData();
		System.out.println(img);

		System.out.println(new Color(0xFFFFFFFF).getRGB());
		System.out.println(new Color(0x00000000).getRGB());
    }*/
}
